home *** CD-ROM | disk | FTP | other *** search
/ CD Ware Multimedia 1995 May / cd Ware (Juegos) Epimundo.iso / DOS / PRGMMING / CNVLIB.ZIP / MYBACK.CMM < prev    next >
Encoding:
Text File  |  1993-07-10  |  27.8 KB  |  931 lines

  1. /***+--------------------------------------------------------MYBACK.CMM
  2.   Copyleft )( Blue Bird Computing, Inc., No rights reserved. Have fun!
  3.  
  4.                         My Little Backup Program
  5.  
  6.   This program will ZIP up files with the Archive bit set in paths
  7.   specified on the command line. It makes sure each Zip file will fit
  8.   on a single floppy and keeps track of what is where so it can do
  9.   incremental backups.
  10.  
  11.   Synopsis:      Cenvi myback.cmm logfile directory1 [... directoryn]
  12.  
  13.   Input:         logfile    - A log file from a previous execution of
  14.                               this program that recorded what files
  15.                               were backed up and what Zip file(s) they
  16.                               may be found in.
  17.  
  18.                  directory1 - A directory with files to be backed up.
  19.                  directoryn - More directories with files to be backed
  20.                               up.
  21.  
  22.   Output:        Messages to stdout.
  23.                  The log file is updated.
  24.                  Zip files are created and copied to floppies.
  25.  
  26.   Calls:         A ZIP utility.
  27.  
  28.   Used by:       My mother (me too!).
  29.  
  30.   Environments:  DOS, OS/2
  31.  
  32.   Dependencies:  This program requires that the following enivronment
  33.                  variables be set:
  34.  
  35.                   1. MYBACK_WORK  - To a fully qualified directoy
  36.                      on the hard disk that can be used as temporary
  37.                      storage while creating the ZIP files. Because
  38.                      of the way ZIP works, this drive must have free
  39.                      space equal to at least the value in MYBACK_SIZE.
  40.  
  41.                   2. MYBACK_DRIVE - To the floppy drive where the
  42.                      created ZIP files will be copied. This must
  43.                      not have a path, but this program doesn't
  44.                      check, so get it right.
  45.                      If this variable is set to "*" (a single
  46.                      asterisk) the ZIP files will be left in the
  47.                      work directory. Useful for unattended backup.
  48.  
  49.                   3. MYBACK_SIZE  - to the maximum size of file that
  50.                      will fit on the floppies you will put in the
  51.                      drive specified by MYBACK_DRIVE. I use 1440000
  52.                      for 3.5" High density drives.
  53.  
  54.                   4. MYBACK_ZIPCMD - to the complete ZIP command,
  55.                      in a special format, that should be used to
  56.                      create the ZIP files. The special format is
  57.                      the ZIP command and options as you would type
  58.                      them on the command line with ZIP file name
  59.                      replaced by "%s" and the file containing the
  60.                      names of files to be zipped replaced by "%s"
  61.                      (as in the printf command.)
  62.                      If you use PKZIP, specify "PKZIP -P %s @%s".
  63.                      If you use ZIP (from info zip) specify
  64.                      "ZIP -@ %s < %s".
  65.  
  66.                   5. MYBACK_SLACK - to the number of bytes you
  67.                      are willing to leave unused on each diskette.
  68.                      Must be a number. MYBACK will stop adding
  69.                      files when it gets this close to the size
  70.                      specified in MYBACK_SIZE.
  71.                      This variable is optional. The default value
  72.                      is 2000 bytes.
  73.  
  74.   Notes:         Use great care in setting the environment variable if
  75.                  you do it in a CMD or BAT file. In particular, to get
  76.                  the proper result for setting MYBACK_ZIPCMD for the
  77.                  ZIP program, you need to use the statement:
  78.  
  79.                    set MYBACK_ZIPCMD=zip -@ %%s ^< %%s
  80.  
  81.                  with the double % signs and the caret(^) in front of
  82.                  the left pointing arrowhead. To set up for PKZIP you
  83.                  also need the double % signs.
  84.  
  85.                  It is recognized that adding files that appear to fit
  86.                  to the zip file incrementaly is a decidedly
  87.                  non-optimal approach.  Some times, it may seem that
  88.                  you are asymtotically approaching completion and will
  89.                  never be done.  It has the advantage, however, of
  90.                  being guaranteed (almost) to work.  (The almost is
  91.                  that it does not account for EAs that ZIP may add to
  92.                  the file.)  This is likely to be a problem only the
  93.                  first time you use it (unless you create humongous
  94.                  amounts of data) because your incremental backups
  95.                  will probably fit in one ZIP file on 1 HD 3.5 disk.
  96.  
  97.                  Use of a reasonable slack value will make the approach
  98.                  to completeion go much faster.
  99.  
  100.   Limitations:   This program will not handle (will deliberately
  101.                  exclude from processing) any file that is bigger than
  102.                  the ZIP file size limit. While it is quite possible
  103.                  that these files could be handled, there is no way
  104.                  to know for sure, and the exception processing code
  105.                  to do this was not considered worth the effort.
  106.  
  107.                  Zip files are identified by date and letter. This
  108.                  means this program can't generate more than 26
  109.                  valid ZIP file names. But it doesn't check, so
  110.                  beware!
  111.  
  112.   Version:
  113.    Revision 0.00  13Jun93  M.A. Stern (Blue Bird Computing)
  114.     Started coding.
  115.  
  116.    Revision 0.01  16Jun93  M.A. Stern (Blue Bird Computing)
  117.     Add code to read log file.
  118.  
  119.    Revision 0.02  22Jun93  M.A. Stern (Blue Bird Computing)
  120.     Add code to really do ZIPping.
  121.     Add code to write updated log file.
  122.  
  123.    Revision 1.00  22Jun93  M.A. Stern (Blue Bird Computing)
  124.     Released to BSN.
  125.  
  126.    Revision 1.01  23Jun93  M.A. Stern (Blue Bird Computing)
  127.     Minor bugfix.
  128.  
  129.    Revision 1.02  26Jun93  M.A. Stern (Blue Bird Computing)
  130.     Support multiple backups on the same day.
  131.     Re-arrange log file to be a little prettier.
  132.     Don't back up files of length 0.
  133.  
  134.    Revision 1.03  04Jul93  M.A. Stern (Blue Bird Computing)
  135.     Change use of System command to be able to support any ZIPper with
  136.      any command format.
  137.  
  138.    Revision 1.04  06Jul93  M.A. Stern (Blue Bird Computing)
  139.     Add use of an environment variable for "slack" in filling
  140.      diskette.
  141.     Add option to leave ZIP files in work dir if disk="*".
  142.  
  143.    Revision 1.05  07Jul93  M.A. Stern (Blue Bird Computing)
  144.     Some performance optimizations.
  145.  
  146.    Revision 1.06  10Jul93  M.A. Stern (Blue Bird Computing)
  147.     Bugfix on performance optimization.
  148.  
  149. ****------------------------------------------------------------------*/
  150.  
  151. /* Initializations */
  152.  
  153. iZipOvhd = 112; /* Not right for all cases, but close. */
  154.  
  155.  
  156. main( argc, argv ) {
  157.  
  158.    if ( argc < 3 ) { Usage(); return 0; }
  159.  
  160.    szLogFile = argv[1];
  161.    cPaths = argc - 2;
  162.    aszPathStr[0] = "";
  163.    SetArraySpan( aszPathStr, cPaths -1 );
  164.    iPathInd = 2;
  165.    iFullback = 0;
  166.  
  167.    /* Make sure environment variables have values. */
  168.  
  169.    if ( !Defined( MYBACK_WORK ) ) {
  170.       printf( "Work Directory for ZIP creation: (MYBACK_WORK)"
  171.               " not set.\n")
  172.       return 1;
  173.       }
  174.    szWorkDir = MYBACK_WORK;
  175.  
  176.    if ( !Defined( MYBACK_DRIVE ) ) {
  177.       printf( "Drive to write ZIP files to: (MYBACK_DRIVE)"
  178.               " not set.\n")
  179.       return 1;
  180.       }
  181.    szBackDisk = MYBACK_DRIVE;
  182.  
  183.    if ( !Defined( MYBACK_SIZE ) ) {
  184.       printf( "ZIP file size limit: (MYBACK_SIZE) not set.\n")
  185.       return 1;
  186.       }
  187.    iZipLimit = MYBACK_SIZE;
  188.  
  189.    if ( !Defined( MYBACK_ZIPCMD ) ) {
  190.       printf( "ZIP command and options: (MYBACK_ZIPCMD)"
  191.               " not set.\n")
  192.       return 1;
  193.       }
  194.  
  195.    if ( !Defined( MYBACK_SLACK ) ) {
  196.       printf( "Diskette slack space: (MYBACK_SLACK) not set.\n")
  197.       printf( "Will use 2000 as default.\n")
  198.       iZipSlack = 2000;
  199.       }
  200.    else {
  201.       iZipSlack = MYBACK_SLACK;
  202.       if ( iZipSlack == 0 ) {
  203.          printf( "Diskette slack space: (MYBACK_SLACK) not valid.\n")
  204.          printf( "Will use 2000 as default.\n")
  205.          iZipSlack = 2000;
  206.          }
  207.       }
  208.  
  209.    /* Validate ZIP command format. */
  210.  
  211.    strcpy( szTest, MYBACK_ZIPCMD );
  212.    szS = strstr( szTest, "%s" );
  213.    if ( szS == NULL ) {
  214.       printf( "ZIP command format is incorrect: no %%s found.\n" );
  215.       return 1;
  216.       }
  217.    szS[0] = ' ';
  218.    szS[1] = ' ';
  219.    szS = strstr( szTest, "%s" );
  220.    if ( szS == NULL ) {
  221.       printf( "ZIP command format is incorrect: second %%s not"
  222.               " found.\n" );
  223.       return 1;
  224.       }
  225.  
  226.    /* Validate the paths entered. */
  227.  
  228.    fError = 0;
  229.  
  230.    for( i=0; i<cPaths; i++, iPathInd++ ) {
  231.  
  232.       aszPathStr[i] = argv[iPathInd];
  233.  
  234.       if ( ValidatePath( aszPathStr[i] ) ) {
  235.          fError = 1;
  236.          printf( "Path spec %s is invalid.\n", argv[iPathInd] );
  237.          }
  238.       else printf( "Files will be backed up from %s\n", aszPathStr[i] );
  239.  
  240.       }
  241.  
  242.    if ( fError ) {
  243.       printf( "%s stopping because of errors.\n", argv[0] );
  244.       return 1;
  245.       }
  246.  
  247.    /* Initialize backup log data structure and array so Cmm doesn't
  248.       add one item at a time when we expand it. */
  249.  
  250.    strcpy( BackLog[0].name, "\177\177\177" );
  251.    BackLog[0].size    = 1;
  252.    BackLog[0].Write   = time();
  253.    strcpy( BackLog[0].zipfile, "99999" );
  254.    BackLog[0].flag    = 0;
  255.  
  256.    /* Initialize max used zip file name to an old value. */
  257.  
  258.    strcpy( MaxZip, "000000_A" );
  259.  
  260.    /* Open the log file and read it in. */
  261.  
  262.    fh = fopen( szLogFile, "r" );
  263.    cIndex = 0;
  264.    if ( fh != NULL ) {
  265.  
  266.       /* Read in the data from the file. */
  267.  
  268.       printf( "Will read backup log file %s\n", szLogFile );
  269.  
  270.       szRec = fgets( fh );
  271.  
  272.       while ( !feof( fh ) ) {
  273.          cItems = sscanf( szRec, " %d %d %s %s ", RecSize, RecWrite,
  274.                           RecZipfile, RecName );
  275.          if ( cItems < 4 )
  276.             printf( "Insufficient data in backup record %s\n", szRec );
  277.          else {
  278.             BackLog[cIndex].size    = RecSize;
  279.             BackLog[cIndex].Write   = RecWrite;
  280.             strcpy( BackLog[cIndex].name, RecName );
  281.             strcpy( BackLog[cIndex].zipfile, RecZipfile );
  282.             cIndex++;
  283.  
  284.             /* Check zip file against largest used so far. */
  285.             /* Save the larger value. */
  286.  
  287.             if ( stricmp( RecZipFile, MaxZip ) > 0 )
  288.                strcpy( MaxZip, RecZipFile );
  289.  
  290.             }
  291.  
  292.          szRec = fgets( fh );
  293.  
  294.          }
  295.  
  296.       fclose( fh );
  297.       }
  298.    else {
  299.       printf( "Backup log file %s could not be opened.\n", szLogFile );
  300.       printf( "Will do full backup.\n" );
  301.       iFullback = 1;
  302.       }
  303.  
  304.    /* Add one more (or the only one) element to the backup log that
  305.       will be greater than everything else to act as a terminal
  306.       anchor. */
  307.  
  308.    strcpy( BackLog[cIndex].name, "\177\177\177" );
  309.    BackLog[cIndex].size    = 1;
  310.    BackLog[cIndex].Write   = time();
  311.    strcpy( BackLog[cIndex].zipfile, "9" );
  312.  
  313.    /* Sort the backup log file by name so we can do simple matching
  314.       to decide what to back up. */
  315.  
  316.    qsort( BackLog, cIndex, "NameCmp" );
  317.  
  318.    /* Get the list of all files that are candidates for backup. */
  319.  
  320.    cFiles = 0;
  321.  
  322.    /* Initialize candidate structure and array so SetArraySpan works
  323.       and we don't have to grow the array one at a time. */
  324.  
  325.    strcpy( Candidates[0].name, "dummy" );
  326.    Candidates[0].size = 1;
  327.    Candidates[0].Write = time();
  328.    Candidates[0].flag = 0;
  329.    strcpy( Candidates[0].zipfile, "0" );
  330.  
  331.    for( i=0; i<cPaths; i++ ) {
  332.  
  333.       iLen = strlen( aszPathStr[i] );
  334.       if ( aszPathStr[i][iLen-1] == '\\' )
  335.          strcpy( szStarStar, "*.*" )
  336.       else
  337.          strcpy( szStarStar, "\\*.*" )
  338.  
  339.       strcat( strcpy( szTemp, aszPathStr[i] ), szStarStar );
  340.  
  341.       ThisSet = Directory( szTemp, TRUE,
  342.                            FATTR_ARCHIVE,
  343.                            FATTR_ARCHIVE );
  344.       if ( ThisSet == NULL ) {
  345.          printf( "No files found in %s\n", aszPathStr[i] );
  346.          continue;
  347.          }
  348.  
  349.       SetSize = GetArraySpan( ThisSet ) +1;
  350.       NewSize = cFiles + SetSize;
  351.       SetArraySpan( Candidates, NewSize-1 );
  352.  
  353.       for( j=0; j<SetSize; j++ ) {
  354.  
  355.          if ( ThisSet[j].size > 0 ) {
  356.  
  357.             strcpy( Candidates[cFiles].name, ThisSet[j].name );
  358.             Candidates[cFiles].size  = ThisSet[j].size;
  359.             Candidates[cFiles].Write = ThisSet[j].Write;
  360.             Candidates[cFiles].flag  = 1;
  361.  
  362.             cFiles++;
  363.             }
  364.  
  365.          }
  366.  
  367.       }
  368.  
  369.    if ( cFiles == 0 ) {
  370.       printf( "No files to back up.\n" );
  371.       return 0;
  372.       }
  373.  
  374.    /* Re-adjust size of candidates array because of possiblilty of
  375.       excluded zero length files. */
  376.  
  377.    SetArraySpan( Candidates, cFiles-1 );
  378.  
  379.    /* Sort the list of files by name so regardless of paths given
  380.       on the command line, they will be in order. */
  381.  
  382.    qsort( Candidates, cFiles, "NameCmp" );
  383.  
  384.    /* Remove any duplicate names that ocurred because of path
  385.       overlap. */
  386.  
  387.    cBefore = cFiles;
  388.    RemoveDuplicates( Candidates, cFiles );
  389.    if ( cFiles != cBefore )
  390.       printf( "%d duplicate files removed.\n", cBefore-cFiles );
  391.  
  392.    /* OK, we have backup candidates and the history of prior backups.
  393.       Lets go through the candidates comparing them to what was backed
  394.       up before and decide what needs to be backed up now. */
  395.  
  396.    cToback = cFiles;
  397.    for( i=0, iInd=0; i<cFiles; i++ ) {
  398.  
  399.       iTest = stricmp( Candidates[i].name, BackLog[iInd].name );
  400.       while ( iTest > 0 ) {
  401.          iInd++;
  402.          iTest = stricmp( Candidates[i].name, BackLog[iInd].name );
  403.          }
  404.  
  405.       if ( iTest == 0 ) {
  406.          /* We have a match on name. If other attributes match, the
  407.             file hasn't changed since it was backed up. Turn off the
  408.             flag, so we won't Zip it up again. */
  409.  
  410.          if ( Candidates[i].size  == BackLog[iInd].size  &&
  411.               Candidates[i].Write == BackLog[iInd].Write ) {
  412.  
  413.             Candidates[i].flag = 0;
  414.             cToback--; /* Decrement backup count. */
  415.  
  416.             } /* if ( Candidates.... */
  417.  
  418.          } /* if ( iTest == 0 ) */
  419.  
  420.       } /* for( i=0, iInd=0; i<cFiles; i++ ) */
  421.  
  422.    /* Make sure there are no files that appear larger than the
  423.       ZIP limit. If there are, remove them as candidates and give
  424.       a message. */
  425.  
  426.    printf( "Checking for large files.\n" );
  427.  
  428.    for( i=0, iInd=0; i<cFiles; i++ ) {
  429.  
  430.       if ( Candidates[i].size > iZipLimit+iZipOvhd ) {
  431.          printf( "File %s is too large to handle.\n",
  432.                  Candidates[i].name );
  433.          Candidates[i].flag = 0;
  434.          cToback--; /* Decrement backup count. */
  435.          }
  436.  
  437.       }
  438.  
  439.    if ( cToBack == 0 ) {
  440.       printf( "No files to back up.\n" );
  441.       return 0;
  442.       }
  443.  
  444.    printf( "%d Files need to be backed up.\n", cToback );
  445.  
  446.    /* Get today's date so we can construct the ZIP file names. */
  447.  
  448.    tm = localtime( time() );
  449.    sprintf( szZipFront, "%2.2d%2.2d%2.2d", tm.tm_year, tm.tm_mon+1,
  450.             tm.tm_mday );
  451.  
  452.    for( i=0; i<6; i++ )
  453.       if ( szZipFront[i] == ' ' ) szZipFront[i] = '0';
  454.  
  455.    /* Check this Zip beginning against MaxZip. Use MaxZip if
  456.       it is bigger. */
  457.  
  458.    strupr( MaxZip );
  459.    if ( strncmp( MaxZip, szZipFront, 6 ) > 0 ) {
  460.       printf( "Using ZIP prefix from backup log file.\n" );
  461.       strncpy( zsZipFront, MaxZip, 6 );
  462.       }
  463.  
  464.    printf( "All ZIP files will start with %s\n", szZipFront );
  465.  
  466.    /* Time to make ZIP files. What we do is gather up enough
  467.       candidate files to have the bytes to fill one disk (with ZIP
  468.       overhead). We then ZIP them up and see if we can add more.
  469.       We repeat this until all files that need to be backed up
  470.       have been put in a ZIP file. */
  471.  
  472.    iZipInd = 0;
  473.    iCreated = 0;
  474.    strcpy( szLstFile, szWorkDir );
  475.    strcat( szLstFile, "\\MYBACK.LST" );
  476.  
  477.    iMinInd = 0;
  478.    while( cToback > 0 ) {
  479.  
  480.       szZipFile = MakeZipName( szZipFront, iZipInd, MaxZip );
  481.       strcpy( szFullZip, szWorkDir );
  482.       strcat( szFullZip, "\\" );
  483.       strcat( szFullZip, szZipFile );
  484.       strcat( szFullZip, ".zip" );  /* So we have it for later. */
  485.       remove( szFullZip );
  486.  
  487.       printf( "About to create ZIP file %s.ZIP\n", szZipFile );
  488.  
  489.       iTot = 0;
  490.       fToAdd = 1;
  491.  
  492.       while( iTot < (iZipLimit-iZipSlack) && fToAdd ) {
  493.  
  494.          iToAdd = AddCand( Candidates, iTot, iZipLimit, iMinInd );
  495.  
  496.          if ( iToAdd > 0 ) {
  497.             printf( "%d files to be added this pass.\n", iToAdd );
  498.             iOK = AddToZip( Candidates, szFullZip, szLstFile, iTot,
  499.                             iToAdd, iMinInd );
  500.             if ( !iOK ) {
  501.                printf( "Errors ocurred add files to zip file %s\n",
  502.                        szFullZip );
  503.                printf( "Program stopping due to errors\n" );
  504.                return 1;
  505.                }
  506.             iTot = GetZipSize( szFullZip );
  507.             MarkZipped( Candidates, szZipFile, iToAdd, iMinInd );
  508.             cToback -= iToAdd;
  509.             }
  510.          else {
  511.             printf( "ZIP file %s is complete.\n", szZipFile );
  512.             fToAdd = 0;
  513.             }
  514.  
  515.          } /* while( iTot < (iZipLimit-iZipSlack) && fToAdd ) */
  516.  
  517.       iTot = GetZipSize( szFullZip );
  518.       if ( iTot > 0 ) {
  519.          if ( strcmp( "*", szBackDisk ) )
  520.             CopyZipToDest( szFullZip, szBackDisk, szZipFile, iTot );
  521.          else printf( "ZIP file left in %s\n", szWorkDir );
  522.          iCreated++
  523.          }
  524.  
  525.       if ( iZipInd > 25 ) {
  526.          printf( "Too many ZIP files for this program.\n" );
  527.          printf( "Stopping.\n" );
  528.          break;
  529.          }
  530.       }
  531.  
  532.    if ( iCreated ) {
  533.  
  534.       printf( "%d ZIP file were created.\nUpdating log file.\n",
  535.               iCreated );
  536.  
  537.       UpdateLogData( Candidates, BackLog );
  538.       WriteBackLog( BackLog, Candidates, szLogFile, iFullback );
  539.  
  540.       }
  541.  
  542.    printf( "Backup complete.\n" );
  543.    return 0;
  544.  
  545.    }
  546.  
  547.  
  548. /*--------------------------------------------------------------------*/
  549. /* Update the backup log with data from the candidates that were
  550.    backed up. */
  551.  
  552. UpdateLogData( Cands, Logs ) {
  553.  
  554.    /* The logic here is to go through both lists (which are still
  555.       sorted) and update log entries for files that have been backed
  556.       up this time around. We don't ADD log entries where they don't
  557.       already exist. Rather we mark these candidate entries as needing
  558.       to be written out to the log later. That way we don't have to
  559.       try and expand the logs array. */
  560.  
  561.    cCand = GetArraySpan( Cands ) +1;
  562.    for( i=0, iInd=0; i<cCand; i++ ) {
  563.  
  564.       /* Note that we already have a search terminator in the
  565.          Logs array from before. */
  566.  
  567.       iTest = stricmp( Cands[i].name, Logs[iInd].name );
  568.       while ( iTest > 0 ) {
  569.          iInd++;
  570.          iTest = stricmp( Cands[i].name, Logs[iInd].name );
  571.          }
  572.  
  573.       if ( iTest == 0 ) {
  574.          /* We have a match on name. If this was backed up (indicated
  575.             by flag=3) we update the logs data with the cadidate data,
  576.             and mark it as already in the log data. */
  577.  
  578.          if ( Cands[i].flag == 3 ) {
  579.  
  580.             Cands[i].flag = 4;
  581.             strcpy( Logs[iInd].zipfile, Cands[i].zipfile );
  582.             Logs[iInd].size    = Cands[i].size;
  583.             Logs[iInd].Write   = Cands[i].Write;
  584.  
  585.             } /* if ( Cands[i].flag == 3 ) */
  586.  
  587.          } /* if ( iTest == 0 ) */
  588.  
  589.       } /* for( i=0, iInd=0; i<cCand; i++ ) */
  590.  
  591.    return;
  592.  
  593.    }
  594.  
  595.  
  596. /*--------------------------------------------------------------------*/
  597. /* Write out the backup log file, re-naming the old version (which
  598.    probably ended up in one of the ZIP files!). */
  599.  
  600. WriteBackLog( Logs, Cands, szFile, iFull ) {
  601.  
  602.    /* The logic here is to write out the log data, followed by any
  603.       additional candidate data (that was backed up and was new) to
  604.       the log file. We could merge the two sets and sort, but why
  605.       bother when we will sort the log when it is read in next time. */
  606.  
  607.    /* First parse the file name and make two alternate file names:
  608.       one for the new file, and one for the "bak" file. */
  609.  
  610.    Parts = SplitFileName( szFile );
  611.  
  612.    OldExt = Parts.ext;
  613.    if ( !stricmp( OldExt, ".BAK" ) )
  614.       strcpy( BakExt, ".BAC" )
  615.    else strcpy( BakExt, ".BAK" )
  616.  
  617.    if ( !stricmp( OldExt, ".$$$" ) )
  618.       strcpy( NewExt, ".$$1" )
  619.    else strcpy( NewExt, ".$$$" )
  620.  
  621.    strcpy( szBakFile, Parts.name );
  622.    strcat( szBakFile, BakExt );
  623.  
  624.    strcpy( szNewFile, Parts.dir );
  625.    strcat( szNewFile, Parts.name );
  626.    strcat( szNewFile, NewExt );
  627.  
  628.    strcpy( szFullBak, Parts.dir );  /* To remove old bak file. */
  629.    strcat( szFullBak, szBakFile );
  630.  
  631.    strcpy( szOldFile, Parts.name );
  632.    strcat( szOldFile, OldExt );
  633.  
  634.    fh = fopen( szNewFile, "w" );
  635.    if ( fh == NULL ) {
  636.       printf( "New log file (%s) could not be opened\n", szNewFile );
  637.       exit(1);
  638.       }
  639.  
  640.    /* Write out the data. */
  641.  
  642.    cLogs = GetArraySpan( Logs ); /* Note no extra 1 for trailer! */
  643.    cCand = GetArraySpan( Cands ) +1;
  644.  
  645.    for( i=0; i<cLogs; i++ )
  646.       fprintf( fh, "%10.d  %10.d  %s  %s \n", Logs[i].size,
  647.                Logs[i].Write, Logs[i].zipfile, Logs[i].name );
  648.  
  649.    for( i=0; i<cCand; i++ )
  650.       if ( Cands[i].flag == 3 )
  651.          fprintf( fh, "%10.d  %10.d  %s  %s \n", Cands[i].size,
  652.                   Cands[i].Write, Cands[i].zipfile, Cands[i].name );
  653.  
  654.    fclose( fh );
  655.  
  656.    /* Now rename the files. */
  657.  
  658.    remove( szFullBak );
  659.    if ( !iFull )
  660.       iRc = rename( szFile, szBakFile );
  661.    else iRc = 0;
  662.    if ( iRc != 0 ) {
  663.       printf( "Rename of %s to %s failed. New log file is %s\n",
  664.               szFile, szBakFile, szNewFile );
  665.       exit(1);
  666.       }
  667.  
  668.    iRc = rename( szNewFile, szOldFile );
  669.    if ( iRc != 0 ) {
  670.       printf( "Rename of %s to %s failed. New log file is %s\n",
  671.               szNewFile, szOldFile, szNewFile );
  672.       exit(1);
  673.       }
  674.  
  675.    return 0;
  676.  
  677.    }
  678.  
  679.  
  680. /*--------------------------------------------------------------------*/
  681. /* Copy a ZIP file to it's final destination and erase it on the disk */
  682.  
  683. CopyZipToDest( szFullZip, szDest, szBaseZip, iSize ) {
  684.  
  685.    strcpy( szSysCmd, "copy " );
  686.    strcat( szSysCmd, szFullZip );
  687.    strcat( szSysCmd, " " );
  688.    strcat( szSysCmd, szDest );
  689.  
  690.    strcpy( szFloppyZip, szDest );
  691.    strcat( szFloppyZip, szBaseZip );
  692.    strcat( szFloppyZip, ".zip" );
  693.  
  694. Retry:
  695.    printf( "Please put a blank formatted diskette in drive %s\n",
  696.            szDest );
  697.    printf( "Press a key when ready..." );
  698.    c = getch();
  699.    printf( "\n" );
  700.  
  701.    system( szSysCmd );
  702.  
  703.    Data = Directory( szFloppyZip, FALSE, FATTR_ARCHIVE, 0);
  704.    if ( Data != NULL )
  705.       if ( Data[0].size == iSize ) {
  706.          printf( "Copy successful.\n" );
  707.          remove( szFullZip );
  708.          return;
  709.          }
  710.  
  711.    printf( "Copy to floppy failed.\n" );
  712.  
  713.    while ( 1 ) {
  714.       printf( "Press Q to quit, R to retry, or I to Ignore: " );
  715.       c = getche();
  716.       if ( c != '\n' ) printf( "\n" );
  717.       c = toupper( c );
  718.       if ( c == 'Q' ) exit(1);
  719.       if ( c == 'R' ) goto Retry;
  720.       if ( c == 'I' ) return;
  721.       }
  722.  
  723.    }
  724.  
  725.  
  726. /*--------------------------------------------------------------------*/
  727. /* Get the actual total bytes of a ZIP file. */
  728.  
  729. GetZipSize( szZip ) {
  730.  
  731.    Size = 0;
  732.    Data = Directory( szZip, FALSE, FATTR_ARCHIVE, 0);
  733.    if ( Data != NULL ) Size = Data[0].size;
  734.    return Size;
  735.  
  736.    }
  737.  
  738.  
  739. /*--------------------------------------------------------------------*/
  740. /* Mark files as being in a ZIP file and save ZIP file name. */
  741.  
  742. MarkZipped( Cands, szZip, iToAdd, iMinIndex ) {
  743.  
  744.    iNumCand = GetArraySpan( Cands ) +1;
  745.    iNum = 0
  746.  
  747.    for( i=iMinIndex; i<iNumCand && iNum<iToAdd; i++ ) {
  748.  
  749.       if ( Cands[i].flag == 2 ) {
  750.  
  751.          Cands[i].flag = 3;
  752.          strcpy( Cands[i].zipfile, szZip );
  753.          iNum++;
  754.  
  755.          }
  756.  
  757.       } /* for( i=iMinIndex; i<iNumCand && iNum<iToAdd; i++ ) */
  758.  
  759.    return;
  760.    }
  761.  
  762.  
  763. /*--------------------------------------------------------------------*/
  764. /* Add marked files to a ZIP file using the system() command. */
  765.  
  766. AddToZip( Cands, szZip, szList, iTot, iToAdd, iMinIndex ) {
  767.  
  768.    iNumCand = GetArraySpan( Cands ) +1;
  769.    iNum = 0
  770.  
  771.    fh = fopen( szList, "w" );
  772.    if ( fh == NULL ) {
  773.       printf( "Could not open list file %s\n", szList );
  774.       return 0;
  775.       }
  776.  
  777.    for( i=iMinIndex; i<iNumCand && iNum<iToAdd; i++ ) {
  778.  
  779.       if ( Cands[i].flag == 2 ) {
  780.          fprintf( fh, "%s\n", Cands[i].name );
  781.          iNum++;
  782.          }
  783.  
  784.       } /* for( i=iMinIndex; i<iNumCand && iNum<iToAdd; i++ ) */
  785.  
  786.    fclose( fh );
  787.  
  788.    if ( iNum == 0 ) {
  789.       printf( "No candidates found to add\n" );
  790.       remove( szList );
  791.       return 0;
  792.       }
  793.  
  794.    /* Do the ZIP command. */
  795.  
  796.    strcpy( szZipCmd, MYBACK_ZIPCMD );
  797.    iRc = system( szZipCmd, szZip, szList );
  798.  
  799.    remove( szList );
  800.    if ( !iRc ) return 1;
  801.    else return 0;
  802.  
  803.    }
  804.  
  805.  
  806. /*--------------------------------------------------------------------*/
  807. /* Determine what files can fit in the current ZIP file on the
  808.    current pass and mark them as such. */
  809.  
  810. AddCand( Cands, iZipCum, iLimit, iMinIndex ) {
  811.  
  812.    iNumCand = GetArraySpan( Cands ) +1;
  813.    iToAdd = 0;
  814.    iNewMin = -1;
  815.  
  816.    for( i=iMinIndex; i<iNumCand && iToAdd<100; i++ ) {
  817.  
  818.       if ( Cands[i].flag == 1 ) {
  819.  
  820.          if ( iNewMin == -1 ) iNewMin = i;
  821.          iBytes = Cands[i].size + iZipOvhd;
  822.  
  823.          if ( iZipCum+iBytes < iLimit ) {
  824.  
  825.             iZipCum+= iBytes;
  826.             Cands[i].flag = 2;
  827.             iToAdd++;
  828.  
  829.             } /* if ( iZipCum+iBytes < iLimit ) */
  830.  
  831.          } /* if ( Cands[i].flag == 1 ) */
  832.  
  833.       } /* for( i=iMinIndex; i<iNumCand; i++ ) */
  834.  
  835.    if ( iToAdd ) iMinIndex = iNewMin;
  836.    return iToAdd;
  837.  
  838.    }
  839.  
  840.  
  841. /*--------------------------------------------------------------------*/
  842. /* Make sequential names for ZIP files. */
  843.  
  844. MakeZipName( szFront, iInd, MaxVal ) {
  845.  
  846. Again:
  847.  
  848.    strcpy( szLetter, " " );
  849.    szLetter[0] = 'A' + iInd;
  850.    strcpy( szZip, szFront );
  851.    strcat( szZip, "_" );
  852.    strcat( szZip, szLetter );
  853.    iInd++;
  854.  
  855.    if ( strcmp( MaxVal, szZip ) >= 0 ) goto Again;
  856.  
  857.    return( szZip );
  858.    }
  859.  
  860.  
  861. /*--------------------------------------------------------------------*/
  862. /* Validate a backup path spec. */
  863.  
  864. ValidatePath( szPath ) {
  865.  
  866.    iLen =strlen( szPath );
  867.    c = szPath[iLen-1];
  868.    if ( c == '\\' ) {
  869.       if ( !(iLen == 3 && szPath[1] == ':' && isalpha( szPath[0] ) ) )
  870.          szPath[iLen-1] = 0;
  871.       }
  872.  
  873.    printf( "Path to check is %s\n", szPath );
  874.  
  875.    /* Check for wildcards. */
  876.  
  877.    if ( strchr( szPath, '*' ) != NULL ) return 1;
  878.    if ( strchr( szPath, '?' ) != NULL ) return 1;
  879.  
  880.    szTemp = FullPath( szPath ) ;
  881.  
  882.    if ( szTemp == NULL ) return 1;
  883.    strcpy( szPath, szTemp );
  884.    return 0;
  885.  
  886.    }
  887.  
  888.  
  889. /*--------------------------------------------------------------------*/
  890. /* Compare the name elements of a structure. */
  891.  
  892. NameCmp( Elem1, Elem2 ) { return stricmp( Elem1.name, Elem2.name ) }
  893.  
  894.  
  895. /*--------------------------------------------------------------------*/
  896. /* Remove duplicate entries by name. The array of structures must
  897.    be sorted. */
  898.  
  899. RemoveDuplicates( WorkStruct, iSize ) {
  900.  
  901.    iTot = iSize;
  902.  
  903.    for( i=1; i<iSize; i++ )
  904.       if ( !stricmp( WorkStruct[i-1].name, WorkStruct[i].name ) ) {
  905.          strcpy( WorkStruct[i-1].name, "\177\177\177" );
  906.          iTot--;
  907.          }
  908.  
  909.    /* Now see if there were any duplicates to remove. If so, they
  910.       will end up at the end of the range when we sort the data, and
  911.       we can lop them off with SetArraySpan. */
  912.  
  913.    if ( iTot != iSize ) {
  914.  
  915.       qsort( WorkStruct, iSize, "NameCmp" );
  916.       SetArraySpan( WorkStruct, iTot-1 );
  917.       iSize = iTot;
  918.       }
  919.  
  920.    }
  921.  
  922.  
  923. /*--------------------------------------------------------------------*/
  924. /* Print usage info. This is real brief right now. */
  925.  
  926. Usage() {
  927.    printf( "Usage:\tMyback logfile directory ....\n" );
  928.    return 0;
  929.    }
  930.  
  931.